/*
 * GuitarX Afinador :: Afinador de guitarra.
 */


/*
 * @(#)CapturePlayback.java	1.11	99/12/03
 *
 * Copyright (c) 1999 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 */

/* Modifications by Iván López for Afinador */ 

import javax.sound.sampled.*;
import javax.swing.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.*;

/**
 *
 * @author Iván López Espejo.
 */
public class Afinador extends javax.swing.JFrame {

    // CONSTANTES.
    // Número de bytes equivalentes a 160 muestras (20ms a 8kHz).
    final int numBytes = 320;
    // Umbral empírico de varianza para la detección de la actividad musical.
    final double THRESHOLD = 0.125;
    // Vector de frecuencias de las cuerdas de la guitarra.
    final double[] freqs = {82.41, 110, 146.83, 196, 246.94, 329.63};
    final double[] ftol = {0.997, 1.003};
    // DATOS MIEMBRO.
    boolean capturando = false;
    int labelAct = 0; // Cuerda activa.
    double pitch; // Valor de pitch actual.
    String errStr;
    ImageIcon notaon;
    ImageIcon notaoff;
    ImageIcon altoon;
    ImageIcon altooff;
    ImageIcon OKon;
    ImageIcon OKoff;
    ImageIcon bajoon;
    ImageIcon bajooff;
    Capture capture = new Capture();
    FormatControls formatControls = new FormatControls();

    /** Método constructor de Afinador */
    public Afinador() {
        // Carga de los elementos gráficos.
        notaon = new ImageIcon(getClass().getResource("/notaon.jpg"));
        notaoff = new ImageIcon(getClass().getResource("/notaoff.jpg"));
        altoon = new ImageIcon(getClass().getResource("/altoon.jpg"));
        altooff = new ImageIcon(getClass().getResource("/altooff.jpg"));
        OKon = new ImageIcon(getClass().getResource("/OKon.jpg"));
        OKoff = new ImageIcon(getClass().getResource("/OKoff.jpg"));
        bajoon = new ImageIcon(getClass().getResource("/bajoon.jpg"));
        bajooff = new ImageIcon(getClass().getResource("/bajooff.jpg"));
        initComponents(); // Inicialización de componentes.
    }

    /**
     * Método para el cálculo de la media de un vector de muestras.
     */
    private static double media(double[] datos) {
        // Cómputo de la media.
        int N = datos.length;
        double med = 0;
        for (int k = 0; k < N; k++) {
            med += datos[k];
        }
        med = med / N;
        return med;
    }

    /**
     * Método para el cálculo de la varianza de un vector de muestras.
     */
    private static double varianza(double[] datos) {
        // Cómputo de la media.
        int N = datos.length;
        double med = media(datos);
        // Cómputo de la varianza.
        double varianza = 0;
        for (int k = 0; k < N; k++) {
            varianza += Math.pow(datos[k] - med, 2);
        }
        varianza = varianza / (N - 1);
        return varianza;
    }

    /**
     * Método para la normalización de un vector de muestras.
     */
    private static double[] normaliza(double[] datos) {
        double maximo = 0;
        for (int k = 0; k < datos.length; k++) {
            if (Math.abs(datos[k]) > maximo) {
                maximo = Math.abs(datos[k]);
            }
        }
        for (int k = 0; k < datos.length; k++) {
            datos[k] = datos[k] / maximo;
        }
        return datos;
    }

    /**
     * Método para enventanar Hamming un vector de datos.
     */
    private static double[] aplicaHamming(double[] datos) {
        double A0 = 0.53836;
        double A1 = 0.46164;
        int Nbf = datos.length;
        for (int k = 0; k < Nbf; k++) {
            datos[k] = datos[k] * (A0 - A1 * Math.cos(2 * Math.PI * k / (Nbf - 1)));
        }
        return datos;
    }

    /**
     * Método para la interpolación de un vector de muestras en un factor 5.
     */
    private static double[] interpola(double[] datos) {
        int lint = 4;
        int rint = 5;
        // Coeficientes del filtro.
        int Ld = datos.length; // Longitud del vector de muestras.
        double[] a = new double[1];
        a[0] = 1;
        double[] b = {0, -0.00376453503361063, -0.00629795036502401, -0.00662615226485741,
            -0.00438587412145353, 0, 0.0211768723715977, 0.0362235123348000, 0.0390784814103427,
            0.0266163981364116, -1.81898940354586e-12, -0.0712703802801116, -0.127577325881248,
            -0.145453022049878, -0.106137926571137, 1.81898940354586e-12, 0.227948353134707,
            0.484381154992661, 0.726001687820826, 0.909647877548196, 0.999999999999091,
            0.909647877548196, 0.726001687820826, 0.484381154992661, 0.227948353134707,
            1.81898940354586e-12, -0.106137926571137, -0.145453022049878, -0.127577325881248,
            -0.0712703802801116, -1.81898940354586e-12, 0.0266163981364116, 0.0390784814103427,
            0.0362235123348000, 0.0211768723715977, 0, -0.00438587412145353, -0.00662615226485741,
            -0.00629795036502401, -0.00376453503361063, 0};
        // Inicializamos el vector de salida.
        double[] odata = new double[5 * Ld];
        for (int k = 0; k < odata.length; k += 5) {
            odata[k] = datos[k / 5];
        }
        double[] od = new double[2 * lint * rint];
        int ind = 2 * lint;
        for (int k = 0; k < od.length; k += rint) {
            od[k] = 2 * datos[0] - datos[ind];
            ind--;
        }
        od = filtrar(b, a, od);
        odata = filtrar(b, a, odata);
        ind = lint * rint;
        for (int k = 0; k < (Ld - lint) * rint; k++) {
            odata[k] = odata[ind];
            ind++;
        }
        for (int k = 0; k < od.length; k++) {
            od[k] = 0;
        }
        ind = Ld - 2;
        for (int k = 0; k < od.length; k += rint) {
            od[k] = 2 * datos[Ld - 1] - datos[ind];
            ind--;
        }
        od = filtrar(b, a, od);
        ind = 0;
        for (int k = Ld * rint - lint * rint; k < 5 * Ld; k++) {
            odata[k] = od[ind];
            ind++;
        }
        return odata;
    }

    /**
     * Método para filtrar FIR ó IIR.
     */
    private static double[] filtrar(double[] b, double[] ap, double[] datos) {
        // Eliminamos el primer coeficiente unidad de ap.
        double[] a = new double[ap.length - 1];
        for (int k = 1; k < ap.length; k++) {
            a[k - 1] = ap[k];
        }
        int p = a.length;
        int q = b.length - 1;
        double[] filtrado = new double[datos.length + p];
        for (int k = 0; k < p; k++) {
            filtrado[k] = 0;
        }
        double[] datos2 = new double[datos.length + q];
        for (int k = 0; k < q; k++) {
            datos2[k] = 0;
        }
        System.arraycopy(datos, 0, datos2, q, datos.length);
        double pB = 0;
        double pA = 0;
        for (int k1 = p; k1 < datos.length + p; k1++) {
            for (int k2 = 0; k2 < q + 1; k2++) {
                pB = pB + b[k2] * datos2[k1 + q - p - k2];
            }
            for (int k3 = 1; k3 < p + 1; k3++) {
                pA = pA - a[k3 - 1] * filtrado[k1 - k3];
            }
            filtrado[k1] = pA + pB;
            pA = pB = 0;
        }
        double[] filtradofinal = new double[datos.length];
        System.arraycopy(filtrado, p, filtradofinal, 0, datos.length);
        return filtradofinal;
    }

    /**
     * Función para el cálculo de la autocorrelación de un vector de muestras.
     * Aplica el estimador de la autocorrelación con sesgo y sólo devuelve la
     * parte positiva del eje de abscisas.
     */
    private static double[] autocorr(double[] datos) {
        int N = datos.length;
        double[] salida = new double[N];
        double suma = 0;
        for (int k = 0; k < N; k++) {
            for (int n = 0; n < N - k; n++) {
                suma = suma + datos[n] * datos[n + k];
            }
            salida[k] = suma / N;
            suma = 0;
        }
        return salida;
    }

    /**
     * Función para la detección de un pico máximo en un vector de muestras.
     */
    private static int detPico(double[] datos) {
        int THRES = 3;
        int sube = 0;
        int baja = 0;
        int pico = 0;
        int prepico = 0;
        for (int n = 0; n < datos.length - 1; n++) {
            if ((datos[n + 1] - datos[n]) > 0) {
                sube++;
                prepico = n + 1;
            }
            if ((datos[n + 1] - datos[n]) < 0) {
                if (sube > THRES) {
                    baja++;
                    if (baja > THRES) {
                        if (pico != 0) {
                            if (datos[pico] < datos[prepico]) {
                                pico = prepico;
                            }
                            sube = 0;
                        } else {
                            pico = prepico;
                            sube = 0;
                        }
                    }
                } else {
                    sube = 0;
                }
            }
        }
        return pico;
    }

    /**
     * Método que implementa la detección del pitch instrumental.
     */
    public double detectorPitch(double[] trama) {
        // Normalización de trama.
        trama = normaliza(trama);
        // Análisis de la trama.
        int picomax;
        double frameVar; // Varianza del frame.
        double[] aunid = new double[1];
        aunid[0] = 1;
        // Detección de la actividad de voz.
        frameVar = varianza(trama);
        if (frameVar > THRESHOLD) {
            // Enventanado del frame mediante Hamming.
            trama = aplicaHamming(trama);
            double[] acorr = autocorr(trama); // Autocorrelación de la excitación.
            double[] acorrinterp = interpola(acorr); // Ganamos resolución temporal.
            picomax = detPico(acorrinterp);
            return (5.0 * 8000.0 / picomax);
        } else {
            return 0;
        }
    }

    /**
     * Clase para tratar el problema de la captura del audio.
     */
    class Capture implements Runnable {

        int anterior = 0; // Último LED encendido.
        int contBytes = 0; // Contador de bytes leídos del buffer de captura.
        byte audioBytes[]; // Bytes de audio leídos.
        TargetDataLine line;
        Thread thread;

        public void start() {
            errStr = null;
            thread = new Thread(this);
            thread.setName("Grabar");
            thread.start();
        }

        public void parar() {
            thread = null;
        }

        private void shutDown(String message) {
            if ((errStr = message) != null && thread != null) {
                thread = null;
                apagaSignl();
                System.err.println(errStr);
            }
        }

        public void run() {
            AudioFormat format = formatControls.getFormat();
            DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
            if (!AudioSystem.isLineSupported(info)) {
                shutDown("Adaptación de línea " + info + " no soportada.");
                return;
            }
            try {
                line = (TargetDataLine) AudioSystem.getLine(info);
                line.open(format, line.getBufferSize());
            } catch (LineUnavailableException ex) {
                shutDown("Imposible abrir la línea: " + ex);
                return;
            } catch (SecurityException ex) {
                shutDown(ex.toString());
                return;
            } catch (Exception ex) {
                shutDown(ex.toString());
                return;
            }
            // Preparación del buffer para captura de audio.
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int frameSizeInBytes = format.getFrameSize();
            int bufferLengthInFrames = line.getBufferSize() / 8;
            int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
            byte[] data = new byte[bufferLengthInBytes];
            int numBytesRead;
            line.start();
            while (thread != null) {
                if ((numBytesRead = line.read(data, 0, bufferLengthInBytes)) == -1) {
                    break;
                }
                out.write(data, 0, numBytesRead);
                contBytes += numBytesRead;
                // Procesado del pitch de la trama.
                if (contBytes >= numBytes) {
                    contBytes = 0;
                    audioBytes = out.toByteArray();
                    data = new byte[bufferLengthInBytes];
                    int nlengthInSamples = audioBytes.length / 2;
                    double[] audioData = null;
                    audioData = new double[nlengthInSamples];
                    for (int i = 0; i < nlengthInSamples; i++) {
                        int MSB = (int) audioBytes[2 * i];
                        int LSB = (int) audioBytes[2 * i + 1];
                        audioData[i] = MSB << 8 | (255 & LSB);
                    }
                    out.reset();
                    pitch = detectorPitch(audioData);
                    if ((pitch != 0) && (labelAct != 0)) {
                        if (pitch < ftol[0] * freqs[labelAct - 1]) {
                            if (anterior != 1) {
                                apagaSignl();
                                jLabel11.setIcon(bajoon);
                                anterior = 1;
                            }
                        } else {
                            if (pitch < ftol[1] * freqs[labelAct - 1]) {
                                if (anterior != 2) {
                                    apagaSignl();
                                    jLabel10.setIcon(OKon);
                                    anterior = 2;
                                }
                            } else {
                                if (anterior != 3) {
                                    apagaSignl();
                                    jLabel9.setIcon(altoon);
                                    anterior = 3;
                                }
                            }
                        }
                    } else {
                        apagaSignl();
                        anterior = 0;
                    }
                }
            }
            // Alcanzamos el final del flujo de audio.
            line.stop();
            line.close();
            line = null;
            // Parar y cerrar el flujo de salida.
            try {
                out.flush();
                out.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    } // Fin de la clase Capturar.

    /**
     * Clase para la fijación del formato del audio tratado.
     */
    public class FormatControls {

        // Devuelve el tipo de formato deseado para almacenamiento.
        public AudioFormat getFormat() {
            float rate = 8000; // Frecuencia de muestreo a 8kHz.
            int sampleSize = 16; // 16 bits por muestra.
            boolean bigEndian = true; // Almacenamiento tipo big-endian.
            int channels = 1; // Mono.
            AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
            return new AudioFormat(encoding, rate, sampleSize, channels, (sampleSize / 8) * channels, rate, bigEndian);
        }
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    /**
     * Método para apagar los LEDs de las notas.
     */
    public void apagaLEDs() {
        jLabel2.setIcon(notaoff);
        jLabel3.setIcon(notaoff);
        jLabel4.setIcon(notaoff);
        jLabel5.setIcon(notaoff);
        jLabel6.setIcon(notaoff);
        jLabel7.setIcon(notaoff);
    }

    /**
     * Método para apagar los LEDs de señalización de la afinación.
     */
    public void apagaSignl() {
        jLabel9.setIcon(altooff);
        jLabel10.setIcon(OKoff);
        jLabel11.setIcon(bajooff);
    }
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jPanel1 = new javax.swing.JPanel();
        jLayeredPane1 = new javax.swing.JLayeredPane();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        jLabel4 = new javax.swing.JLabel();
        jLabel5 = new javax.swing.JLabel();
        jLabel6 = new javax.swing.JLabel();
        jLabel7 = new javax.swing.JLabel();
        jLabel8 = new javax.swing.JLabel();
        jLabel9 = new javax.swing.JLabel();
        jLabel10 = new javax.swing.JLabel();
        jLabel11 = new javax.swing.JLabel();
        jLabel1 = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("GuitarX Afinador :: Iván López Espejo :: babyroller.blogspot.com");
        setResizable(false);

        jLabel2.setIcon(new javax.swing.ImageIcon(getClass().getResource("/notaoff.jpg"))); // NOI18N
        jLabel2.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jLabel2MouseClicked(evt);
            }
        });
        jLabel2.setBounds(268, 226, 25, 20);
        jLayeredPane1.add(jLabel2, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel3.setIcon(new javax.swing.ImageIcon(getClass().getResource("/notaoff.jpg"))); // NOI18N
        jLabel3.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jLabel3MouseClicked(evt);
            }
        });
        jLabel3.setBounds(313, 226, 25, 20);
        jLayeredPane1.add(jLabel3, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel4.setIcon(new javax.swing.ImageIcon(getClass().getResource("/notaoff.jpg"))); // NOI18N
        jLabel4.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jLabel4MouseClicked(evt);
            }
        });
        jLabel4.setBounds(358, 226, 25, 20);
        jLayeredPane1.add(jLabel4, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel5.setIcon(new javax.swing.ImageIcon(getClass().getResource("/notaoff.jpg"))); // NOI18N
        jLabel5.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jLabel5MouseClicked(evt);
            }
        });
        jLabel5.setBounds(406, 226, 25, 20);
        jLayeredPane1.add(jLabel5, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel6.setIcon(new javax.swing.ImageIcon(getClass().getResource("/notaoff.jpg"))); // NOI18N
        jLabel6.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jLabel6MouseClicked(evt);
            }
        });
        jLabel6.setBounds(452, 226, 25, 20);
        jLayeredPane1.add(jLabel6, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel7.setIcon(new javax.swing.ImageIcon(getClass().getResource("/notaoff.jpg"))); // NOI18N
        jLabel7.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jLabel7MouseClicked(evt);
            }
        });
        jLabel7.setBounds(495, 226, 25, 20);
        jLayeredPane1.add(jLabel7, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel8.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jLabel8MouseClicked(evt);
            }
        });
        jLabel8.setBounds(184, 214, 30, 30);
        jLayeredPane1.add(jLabel8, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel9.setIcon(new javax.swing.ImageIcon(getClass().getResource("/altooff.jpg"))); // NOI18N
        jLabel9.setBounds(532, 227, 20, 20);
        jLayeredPane1.add(jLabel9, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel10.setIcon(new javax.swing.ImageIcon(getClass().getResource("/OKoff.jpg"))); // NOI18N
        jLabel10.setBounds(572, 227, 20, 21);
        jLayeredPane1.add(jLabel10, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel11.setIcon(new javax.swing.ImageIcon(getClass().getResource("/bajooff.jpg"))); // NOI18N
        jLabel11.setBounds(612, 227, 20, 21);
        jLayeredPane1.add(jLabel11, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/backg.jpg"))); // NOI18N
        jLabel1.setBounds(0, 0, 800, 260);
        jLayeredPane1.add(jLabel1, javax.swing.JLayeredPane.DEFAULT_LAYER);

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jLayeredPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 800, Short.MAX_VALUE)
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jLayeredPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 259, Short.MAX_VALUE)
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    /**
     * Botón para apagar el afinador.
     * @param evt
     */
    private void jLabel8MouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jLabel8MouseClicked
        System.exit(0);
    }//GEN-LAST:event_jLabel8MouseClicked

    /**
     * Afinación del Mi2.
     * @param evt
     */
    private void jLabel2MouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jLabel2MouseClicked
        apagaLEDs();
        if (labelAct != 1) {
            labelAct = 1;
            if (!capturando) {
                capture.start();
                capturando = true;
            }
            jLabel2.setIcon(notaon);
        } else {
            jLabel2.setIcon(notaoff);
            capture.parar();
            labelAct = 0;
            capturando = false;
        }
    }//GEN-LAST:event_jLabel2MouseClicked

    /**
     * Afinación del La2.
     * @param evt
     */
    private void jLabel3MouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jLabel3MouseClicked
        apagaLEDs();
        if (labelAct != 2) {
            labelAct = 2;
            if (!capturando) {
                capture.start();
                capturando = true;
            }
            jLabel3.setIcon(notaon);
        } else {
            jLabel3.setIcon(notaoff);
            capture.parar();
            labelAct = 0;
            capturando = false;
        }
    }//GEN-LAST:event_jLabel3MouseClicked

    /**
     * Afinación del Re3.
     * @param evt
     */
    private void jLabel4MouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jLabel4MouseClicked
        apagaLEDs();
        if (labelAct != 3) {
            labelAct = 3;
            if (!capturando) {
                capture.start();
                capturando = true;
            }
            jLabel4.setIcon(notaon);
        } else {
            jLabel4.setIcon(notaoff);
            capture.parar();
            labelAct = 0;
            capturando = false;
        }
    }//GEN-LAST:event_jLabel4MouseClicked

    /**
     * Afinación del Sol3.
     * @param evt
     */
    private void jLabel5MouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jLabel5MouseClicked
        apagaLEDs();
        if (labelAct != 4) {
            labelAct = 4;
            if (!capturando) {
                capture.start();
                capturando = true;
            }
            jLabel5.setIcon(notaon);
        } else {
            jLabel5.setIcon(notaoff);
            capture.parar();
            labelAct = 0;
            capturando = false;
        }
    }//GEN-LAST:event_jLabel5MouseClicked

    /**
     * Afinación del Si3.
     * @param evt
     */
    private void jLabel6MouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jLabel6MouseClicked
        apagaLEDs();
        if (labelAct != 5) {
            labelAct = 5;
            if (!capturando) {
                capture.start();
                capturando = true;
            }
            jLabel6.setIcon(notaon);
        } else {
            jLabel6.setIcon(notaoff);
            capture.parar();
            labelAct = 0;
            capturando = false;
        }
    }//GEN-LAST:event_jLabel6MouseClicked

    /**
     * Afinación del Mi4.
     * @param evt
     */
    private void jLabel7MouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jLabel7MouseClicked
        apagaLEDs();
        if (labelAct != 6) {
            labelAct = 6;
            if (!capturando) {
                capture.start();
                capturando = true;
            }
            jLabel7.setIcon(notaon);
        } else {
            jLabel7.setIcon(notaoff);
            capture.parar();
            labelAct = 0;
            capturando = false;
        }
    }//GEN-LAST:event_jLabel7MouseClicked

    /**
     * Método main.
     */
    public static void main(String args[]) {
        JFrame.setDefaultLookAndFeelDecorated(true);
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                try {
                    UIManager.setLookAndFeel("org.pushingpixels.substance.api.skin.SubstanceTwilightLookAndFeel");
                } catch (Exception e) {
                    System.out.println(e.toString());
                }
                Afinador programa = new Afinador();
                BufferedImage icono = null;
                try {
                    icono = ImageIO.read(programa.getClass().getResource("/AppIcono.png"));
                } catch (IOException e) {
                    e.printStackTrace();
                }
                programa.setIconImage(icono);
                programa.setSize(808, 293);
                programa.setLocation(200, 200);
                programa.setVisible(true);

            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel10;
    private javax.swing.JLabel jLabel11;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JLabel jLabel8;
    private javax.swing.JLabel jLabel9;
    private javax.swing.JLayeredPane jLayeredPane1;
    private javax.swing.JPanel jPanel1;
    // End of variables declaration//GEN-END:variables
}
